import pandas as pd
import numpy as np
import json
import re
from datetime import datetime
import matplotlib.pyplot as plt
import seaborn as sns
import os
from utils import ensure_dir


def preprocess_dataframe(df, verbose=True):
    """
    对给定的DataFrame进行全面的数据质量评估和预处理
    
    参数:
    df (DataFrame): 需要处理的数据框
    verbose (bool): 是否打印详细的预处理报告
    
    返回:
    tuple: (处理后的DataFrame, 预处理报告字典)
    """
    # 创建原始数据副本，避免修改原始数据
    df_original = df.copy()
    df_processed = df.copy()
    
    # 初始化报告字典
    report = {
        "原始数据量": len(df_original),
        "原始列": df_original.columns.tolist(),
        "数据类型": df_original.dtypes.to_dict(),
        "缺失值统计": {},
        "异常值统计": {},
        "重复记录": 0,
        "数据转换": [],
        "删除记录": {"理由": [], "数量": []}
    }
    
    if verbose:
        print("="*80)
        print("数据预处理报告")
        print("="*80)
        print(f"原始数据量: {len(df_original)} 行, {len(df_original.columns)} 列")
    
    # 步骤1: 检查重复数据
    duplicates = df_processed.duplicated().sum()
    report["重复记录"] = duplicates
    
    if duplicates > 0:
        if verbose:
            print(f"\n发现 {duplicates} 条重复记录 ({duplicates/len(df_original):.2%})")
        
        # 删除重复数据
        df_processed = df_processed.drop_duplicates()
        report["删除记录"]["理由"].append("重复记录")
        report["删除记录"]["数量"].append(duplicates)
        
        if verbose:
            print(f"已删除重复记录，剩余数据量: {len(df_processed)}")
    
    # 步骤2: 检查缺失值
    missing_data = df_processed.isnull().sum()
    missing_percentage = df_processed.isnull().mean().round(4) * 100
    
    if verbose:
        print("\n缺失值统计:")
    
    for column in df_processed.columns:
        missing_count = missing_data[column]
        missing_percent = missing_percentage[column]
        report["缺失值统计"][column] = {
            "数量": int(missing_count),
            "百分比": missing_percent
        }
        
        if verbose and missing_count > 0:
            print(f"  {column}: {missing_count} 条缺失 ({missing_percent:.2f}%)")
    
    # 步骤3: 处理特定列的数据类型和格式
    
    # 3.1 处理日期列
    date_columns = ['registration_date', 'last_login']
    for col in date_columns:
        if col in df_processed.columns:
            invalid_dates_before = df_processed[~df_processed[col].astype(str).str.match(r'^\d{4}-\d{2}-\d{2}')].shape[0]
            
            # 转换日期格式
            df_processed[col] = pd.to_datetime(df_processed[col], errors='coerce')
            
            # 计算转换后的无效日期数量
            invalid_dates_after = df_processed[col].isna().sum()
            
            if invalid_dates_before > 0 or invalid_dates_after > 0:
                report["数据转换"].append(f"{col}列转换为日期类型，无效日期数量: {invalid_dates_after}")
                
                if verbose:
                    print(f"\n{col}列: 转换为日期类型")
                    print(f"  发现 {invalid_dates_after} 条无效日期 ({invalid_dates_after/len(df_processed):.2%})")
    
    # 3.2 处理数值列
    numeric_columns = ['age', 'income']
    for col in numeric_columns:
        if col in df_processed.columns:
            # 原始非数值数据数量
            non_numeric_before = df_processed[pd.to_numeric(df_processed[col], errors='coerce').isna()].shape[0]
            
            # 转换为数值类型
            df_processed[col] = pd.to_numeric(df_processed[col], errors='coerce')
            
            # 计算转换后的非数值数据数量
            non_numeric_after = df_processed[col].isna().sum()
            
            if non_numeric_before > 0:
                report["数据转换"].append(f"{col}列转换为数值类型，无效值数量: {non_numeric_after}")
                
                if verbose:
                    print(f"\n{col}列: 转换为数值类型")
                    print(f"  发现 {non_numeric_before} 条非数值数据 ({non_numeric_before/len(df_processed):.2%})")
    
    # 步骤4: 处理异常值
    
    # 4.1 年龄异常值
    if 'age' in df_processed.columns:
        # 合理的年龄范围是0-120
        age_outliers = df_processed[(df_processed['age'] < 0) | (df_processed['age'] > 120)].shape[0]
        
        report["异常值统计"]["age"] = {
            "数量": age_outliers,
            "百分比": age_outliers/len(df_processed) * 100 if len(df_processed) > 0 else 0,
            "处理方法": "替换为NaN"
        }
        
        if age_outliers > 0 and verbose:
            print(f"\n年龄列: 发现 {age_outliers} 条异常值 ({age_outliers/len(df_processed):.2%})")
            print("  异常定义: 年龄 < 0 或 年龄 > 120")
            print("  处理方法: 替换为NaN")
        
        # 将异常年龄替换为NaN
        df_processed.loc[(df_processed['age'] < 0) | (df_processed['age'] > 120), 'age'] = np.nan
    
    # 4.2 收入异常值（使用IQR方法）
    if 'income' in df_processed.columns:
        Q1 = df_processed['income'].quantile(0.25)
        Q3 = df_processed['income'].quantile(0.75)
        IQR = Q3 - Q1
        
        lower_bound = Q1 - 1.5 * IQR
        upper_bound = Q3 + 1.5 * IQR
        
        income_outliers = df_processed[(df_processed['income'] < lower_bound) | 
                                       (df_processed['income'] > upper_bound)].shape[0]
        
        report["异常值统计"]["income"] = {
            "数量": income_outliers,
            "百分比": income_outliers/len(df_processed) * 100 if len(df_processed) > 0 else 0,
            "处理方法": "保留但标记",
            "下限": float(lower_bound),
            "上限": float(upper_bound)
        }
        
        if income_outliers > 0 and verbose:
            print(f"\n收入列: 发现 {income_outliers} 条异常值 ({income_outliers/len(df_processed):.2%})")
            print(f"  异常定义: 收入 < {lower_bound:.2f} 或 收入 > {upper_bound:.2f} (IQR方法)")
            print("  处理方法: 保留但添加'income_is_outlier'标记列")
        
        # 添加异常值标记列，而不是删除
        df_processed['income_is_outlier'] = ((df_processed['income'] < lower_bound) | 
                                            (df_processed['income'] > upper_bound))
    
    # 4.3 检查JSON格式的列
    json_columns = ['purchase_history', 'login_history']
    for col in json_columns:
        if col in df_processed.columns:
            # 检查能否成功解析为JSON
            def is_valid_json(x):
                if pd.isna(x):
                    return False
                try:
                    json.loads(x)
                    return True
                except:
                    return False
            
            invalid_json = df_processed[~df_processed[col].apply(is_valid_json)].shape[0]
            
            report["异常值统计"][col] = {
                "数量": invalid_json,
                "百分比": invalid_json/len(df_processed) * 100 if len(df_processed) > 0 else 0,
                "处理方法": "替换为空JSON对象"
            }
            
            if invalid_json > 0 and verbose:
                print(f"\n{col}列: 发现 {invalid_json} 条无效JSON格式 ({invalid_json/len(df_processed):.2%})")
                print("  处理方法: 替换为空JSON对象 {}")
            
            # 替换无效JSON为空对象
            df_processed[col] = df_processed[col].apply(
                lambda x: x if is_valid_json(x) else "{}"
            )
    
    # 4.4 检查电子邮件格式
    if 'email' in df_processed.columns:
        email_pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
        invalid_emails = df_processed[~df_processed['email'].astype(str).str.match(email_pattern)].shape[0]
        
        report["异常值统计"]["email"] = {
            "数量": invalid_emails,
            "百分比": invalid_emails/len(df_processed) * 100 if len(df_processed) > 0 else 0,
            "处理方法": "标记为异常"
        }
        
        if invalid_emails > 0 and verbose:
            print(f"\n邮箱列: 发现 {invalid_emails} 条异常格式 ({invalid_emails/len(df_processed):.2%})")
            print("  处理方法: 添加'invalid_email'标记列")
        
        # 添加无效邮箱标记列
        df_processed['invalid_email'] = ~df_processed['email'].astype(str).str.match(email_pattern)
    
    # 4.5 检查电话号码格式
    if 'phone_number' in df_processed.columns:
        # 简单的电话号码模式：允许+号开头，然后是数字、空格、括号和破折号
        phone_pattern = r'^\+?[\d\s\(\)\-]{5,20}$'
        invalid_phones = df_processed[~df_processed['phone_number'].astype(str).str.match(phone_pattern)].shape[0]
        
        report["异常值统计"]["phone_number"] = {
            "数量": invalid_phones,
            "百分比": invalid_phones/len(df_processed) * 100 if len(df_processed) > 0 else 0,
            "处理方法": "标记为异常"
        }
        
        if invalid_phones > 0 and verbose:
            print(f"\n电话号码列: 发现 {invalid_phones} 条异常格式 ({invalid_phones/len(df_processed):.2%})")
            print("  处理方法: 添加'invalid_phone'标记列")
        
        # 添加无效电话标记列
        df_processed['invalid_phone'] = ~df_processed['phone_number'].astype(str).str.match(phone_pattern)
    
    # 步骤5: 删除严重异常的记录
    # 定义严重异常：同时存在多个关键字段问题的记录
    critical_columns = ['email', 'age', 'registration_date']
    
    # 关键列中存在NaN的记录
    critical_missing = df_processed[critical_columns].isna().sum(axis=1) >= 2
    critical_missing_count = critical_missing.sum()
    
    if critical_missing_count > 0:
        if verbose:
            print(f"\n发现 {critical_missing_count} 条记录同时缺失多个关键字段 ({critical_missing_count/len(df_processed):.2%})")
            print("  关键字段: email, age, registration_date")
            print("  处理方法: 删除这些记录")
        
        # 删除同时缺失多个关键字段的记录
        df_processed = df_processed[~critical_missing]
        report["删除记录"]["理由"].append("同时缺失多个关键字段")
        report["删除记录"]["数量"].append(int(critical_missing_count))
    
    # 步骤6: 最终报告
    final_record_count = len(df_processed)
    removed_records = len(df_original) - final_record_count
    
    report["预处理后数据量"] = final_record_count
    report["删除记录总数"] = removed_records
    report["删除比例"] = removed_records/len(df_original) if len(df_original) > 0 else 0
    
    if verbose:
        print("\n"+"="*80)
        print("预处理完成摘要:")
        print(f"原始数据量: {len(df_original)} 行")
        print(f"预处理后数据量: {final_record_count} 行")
        print(f"删除数据量: {removed_records} 行 ({removed_records/len(df_original):.2%})")
        print("="*80)
    
    return df_processed, report


def visualize_preprocessing_report(report, figsize=(18, 12), savedir=None):
    """
    可视化预处理报告的结果
    
    参数:
    report (dict): 预处理函数生成的报告字典
    figsize (tuple): 图表尺寸
    """
    fig, axes = plt.subplots(2, 2, figsize=figsize)
    
    # 图1: 缺失值比例
    missing_data = pd.DataFrame({
        '列名': list(report['缺失值统计'].keys()),
        '缺失比例': [stats['百分比'] for stats in report['缺失值统计'].values()]
    }).sort_values('缺失比例', ascending=False)
    
    sns.barplot(x='缺失比例', y='列名', data=missing_data, ax=axes[0, 0])
    axes[0, 0].set_title('各字段缺失值比例 (%)')
    axes[0, 0].set_xlim(0, max(missing_data['缺失比例']) * 1.1)
    
    # 图2: 异常值统计
    if report['异常值统计']:
        anomaly_data = pd.DataFrame({
            '字段': list(report['异常值统计'].keys()),
            '异常比例': [stats['百分比'] for stats in report['异常值统计'].values()]
        }).sort_values('异常比例', ascending=False)
        
        sns.barplot(x='异常比例', y='字段', data=anomaly_data, ax=axes[0, 1])
        axes[0, 1].set_title('各字段异常值比例 (%)')
        axes[0, 1].set_xlim(0, max(anomaly_data['异常比例']) * 1.1)
    else:
        axes[0, 1].text(0.5, 0.5, '无异常值统计', ha='center', va='center')
    
    # 图3: 删除记录原因分布
    if report['删除记录']['理由']:
        deletion_data = pd.DataFrame({
            '删除原因': report['删除记录']['理由'],
            '删除记录数': report['删除记录']['数量']
        })
        
        sns.barplot(x='删除记录数', y='删除原因', data=deletion_data, ax=axes[1, 0])
        axes[1, 0].set_title('删除记录原因分布')
    else:
        axes[1, 0].text(0.5, 0.5, '无删除记录', ha='center', va='center')
    
    # 图4: 数据量变化
    axes[1, 1].bar(['原始数据', '处理后数据'], [report['原始数据量'], report['预处理后数据量']])
    axes[1, 1].set_title('数据量变化')
    for i, v in enumerate([report['原始数据量'], report['预处理后数据量']]):
        axes[1, 1].text(i, v + 5, str(v), ha='center')
    
    plt.tight_layout()
    if savedir is not None:
        plt.savefig(ensure_dir(os.path.join(savedir, 'preprocess_report.jpg')))
    try:
        fig.show()
    except Exception as e:
        print(e)#debug
        pass
    

# 使用示例
# df_processed, report = preprocess_dataframe(df)
# visualize_preprocessing_report(report)

